The R markdown is available from the pulldown menu for Code at the upper-right, choose “Download Rmd”, or download the Rmd from GitHub.
Cytoscape is an open source software platform for integrating, visualizing, and analyzing measurement data in the context of networks. This tutorial presents a scenario of how expression and network data can be combined to tell a biological story and includes these concepts:
- Visualizing networks using expression data.
- Filtering networks based on expression data.
- Assessing expression data in the context of a biological network.

Installation
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
if(!"RCy3" %in% installed.packages())
BiocManager::install("RCy3")
library(RCy3)
Getting started
First, launch Cytoscape and keep it running whenever using RCy3. Confirm that you have everything installed and running:
cytoscapePing()
cytoscapeVersionInfo()
Loading Network
Let’s open a Cytoscape demo session file:
Visualizing Expression Data on Networks
Probably the most common use of expression data in Cytoscape is to set the visual style of the nodes (color, shape, border) in a network according to available data. This creates a powerful visualization, portraying functional relation and experimental response at the same time. Here, we will show an example of doing this.
The data used in this example is from yeast, and represents an experiment of perturbations of the genes Gal1, Gal4, and Gal80, which are all yeast transcription factors.
For this tutorial, the experimental data was part of the Cytoscape session file you loaded earlier, and is visible in the Node Table:
You can select nodes in the network by
selectNodes(c("YDL194W", "YLR345W"), by.col = "name")
Selecting one or more nodes in the network will update the Node Table to show only the corresponding row(s).
We can now use the data to manipulate the visual properties of the network by mapping specific data columns to visual style properties:
The gal80Rexp expression values will be mapped to node color; nodes with low expression will be colored blue, nodes with high expression will be colored red. Significance for expression values will be mapped to Node Border Width, so nodes with significant changes will appear with a thicker border.
Set Node Fill Color
Let’s specify the node fill color as a gradient ranging from blue to red for expression values using a continuous mapping for the ‘gal80Rexp’ column:
gal80Rexp.score.table <- getTableColumns('node','gal80Rexp')
gal80Rexp.min <- min(gal80Rexp.score.table, na.rm = T)
gal80Rexp.max <- max(gal80Rexp.score.table, na.rm = T)
data.values = c(gal80Rexp.min, 0, gal80Rexp.max)
display.brewer.all(length(data.values), colorblindFriendly=TRUE, type="div") # div,qual,seq,all
node.colors <- c(rev(brewer.pal(length(data.values), "RdBu")))
setNodeColorMapping('gal80Rexp', data.values, node.colors, style.name="galFiltered Style")
This produces an initial gradient ranging from blue to red for expression values. Notice that the nodes in the network change color.
Set Default Node Color
Some nodes in the network don’t have any data, and for those nodes, the default color applies. In our case, the default color is blue, which falls within the spectrum of our blue-red gradient. This is not ideal for data visualization, so a useful trick is to choose a color outside the gradient spectrum to distinguish nodes with no defined expression value.
We can set default node color to light gray color:
setNodeColorDefault('#D3D3D3', style.name = "galFiltered Style")
Set Node Border Width
To visualize the significance of measurements, let’s add a contiuous mapping for ‘gal80Rsig’ p-values to Node Border Width and Node Border Color:
gal80Rsig.score.table <- getTableColumns('node','gal80Rsig')
gal80Rsig.min <- min(gal80Rsig.score.table, na.rm = T)
gal80Rsig.max <- max(gal80Rsig.score.table, na.rm = T)
data.values <- c(gal80Rsig.min, 0.05, gal80Rsig.sig)
border.colors <- c(rev(brewer.pal(3, "Reds")))
border.width <- c(10,10,2)
setNodeBorderColorMapping('gal80Rsig',data.values,border.colors,style.name="galFiltered Style")
setNodeBorderWidthMapping('gal80Rsig',data.values,border.width,style.name="galFiltered Style")
The significant node now have a thicker, dark red border:
Layouts
An important aspect of network visualization is the layout, meaning the positioning of nodes and edges. Our network had a preset layout in the original file you imported, but this can be changed.
- Let’s change the layout to Degree Sorted Circle Layout:
layoutNetwork("degree-circle")
In this layout, nodes are sorted by degree (connectedness), with the highest degree node at the 6 o’clock position, and remaining nodes are sorted counter clock-wise based on decreasing degree.
For this network, a degree-sorted circle layout may not be the most effective. Instead, let’s try a force-directed layout instead, which may work better with this network.
layoutNetwork("force-directed")
Cytoscape supports many different layout algorithms, described in detail in the Cytoscape manual.
Select Nodes
Cytoscape allows you to easily filter and select nodes and edges based on data attributes. Next, we will select a subset of nodes with high expression in the gal80 knockout:
Let’s select a subset of nodes with high expression in the gal80 knockout:
createColumnFilter('myFilter', 'gal80Rexp', 2.00, "GREATER_THAN")
You should now see only a few nodes in the network selected (highlighted yellow).
Expand Selection and Create New Network
We have now selected only the few top expressing nodes. To see the context of these nodes in the larger network, we can expand the selection of nodes to include the nodes connecting to the selected nodes, i.e. the first neighbors. Once we have that larger selection, we can create a new network.
Select the first neighbors of selected nodes:
We can now create a network from the selection:
createSubnetwork(nodes="selected", subnetwork.name='galFiltered sub')
Digging into the biology of this network, it turns out that GAL4 is repressed by GAL80. Both nodes (GAL4 and GAL11) show fairly small changes in expression, and neither change is statistically significant: they are pale blue with thin borders. These slight changes in expression suggest that the critical change affecting the red nodes might be somewhere else in the network, and not either of these nodes. GAL4 interacts with GAL80, which shows a significant level of repression: it is medium blue with a thicker border.
Note that while GAL80 shows evidence of significant repression, most nodes interacting with GAL4 show significant levels of induction: they are rendered as red rectangles. GAL11 is a general transcription co-factor with many interactions.
Putting all of this together, we see that the transcriptional activation activity of Gal4 is repressed by Gal80. So, repression of Gal80 increases the transcriptional activation activity of Gal4. Even though the expression of Gal4 itself did not change much, the Gal4 transcripts were much more likely to be active transcription factors when Gal80 was repressed. This explains why there is so much up-regulation in the vicinity of Gal4.
Summary
In summary, we have:
- Explored a yeast interactome from a transcription factor knockout experiment
- Created a visual style using expression value as node color and with border width mapped to significance
- Selected high expressing genes and their neighbors and created a new network
Finally, we can now export this network as a publication-quality image….
Saving Results
Cytoscape provides a number of ways to save results and visualizations:
saveSession('./basic-data-visualization.cys')
exportImage('./basic-data-visualization', 'PDF')
exportImage('./basic-data-visualization', 'PNG')
exportImage('./basic-data-visualization', 'JPEG')
exportImage('./basic-data-visualization', 'SVG')
exportImage('./basic-data-visualization', 'PS')
exportNetworkToNDEx("user", "pass", TRUE)
- As a graph format file (Formats: “CX JSON”, “Cytoscape.js JSON”, “GraphML”, “XGMML”, “SIF”,…):
exportNetwork('./basic-data-visualization', 'CX')
exportNetwork('./basic-data-visualization', 'cyjs')
exportNetwork('./basic-data-visualization', 'graphML')
exportNetwork('./basic-data-visualization', 'xGMML')
exportNetwork('./basic-data-visualization', 'SIF')
LS0tCnRpdGxlOiAiQmFzaWMgRGF0YSBWaXN1YWxpemF0aW9uIgphdXRob3I6ICJLb3pvIE5pc2hpZGEsIEtyaXN0aW5hIEhhbnNwZXJzIGFuZCBBbGV4IFBpY28iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIEJpb2NTdHlsZTo6aHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA0CiAgICBkZl9wcmludDogcGFnZWQKLS0tCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBldmFsPUZBTFNFCikKYGBgCipUaGUgUiBtYXJrZG93biBpcyBhdmFpbGFibGUgZnJvbSB0aGUgcHVsbGRvd24gbWVudSBmb3IqIENvZGUgKmF0IHRoZSB1cHBlci1yaWdodCwgY2hvb3NlICJEb3dubG9hZCBSbWQiLCBvciBbZG93bmxvYWQgdGhlIFJtZCBmcm9tIEdpdEh1Yl0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2N5dG9zY2FwZS9jeXRvc2NhcGUtYXV0b21hdGlvbi9tYXN0ZXIvZm9yLXNjcmlwdGVycy9SL25vdGVib29rcy9iYXNpYy1kYXRhLXZpc3VhbGl6YXRpb24uUm1kKS4qCgo8aHIgLz4KCkN5dG9zY2FwZSBpcyBhbiBvcGVuIHNvdXJjZSBzb2Z0d2FyZSBwbGF0Zm9ybSBmb3IgaW50ZWdyYXRpbmcsIHZpc3VhbGl6aW5nLCBhbmQgYW5hbHl6aW5nIG1lYXN1cmVtZW50IGRhdGEgaW4gdGhlIGNvbnRleHQgb2YgbmV0d29ya3MuIFRoaXMgdHV0b3JpYWwgcHJlc2VudHMgYSBzY2VuYXJpbyBvZiBob3cgZXhwcmVzc2lvbiBhbmQgbmV0d29yayBkYXRhIGNhbiBiZSBjb21iaW5lZCB0byB0ZWxsIGEgYmlvbG9naWNhbCBzdG9yeSBhbmQgaW5jbHVkZXMgdGhlc2UgY29uY2VwdHM6CgogLSBWaXN1YWxpemluZyBuZXR3b3JrcyB1c2luZyBleHByZXNzaW9uIGRhdGEuCiAtIEZpbHRlcmluZyBuZXR3b3JrcyBiYXNlZCBvbiBleHByZXNzaW9uIGRhdGEuCiAtIEFzc2Vzc2luZyBleHByZXNzaW9uIGRhdGEgaW4gdGhlIGNvbnRleHQgb2YgYSBiaW9sb2dpY2FsIG5ldHdvcmsuCgohW10oLi9kYXRhL2ltZy9iYXNpYy1kYXRhdml6LXN1Ym5ldC5wbmcpCgo8aHIgLz4KCiMgSW5zdGFsbGF0aW9uCmBgYHtyLCBldmFsID0gRkFMU0V9CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiQmlvY01hbmFnZXIiLCBxdWlldGx5ID0gVFJVRSkpCiAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQoKaWYoISJSQ3kzIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKQogIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJSQ3kzIikKCmxpYnJhcnkoUkN5MykKYGBgCgojIEdldHRpbmcgc3RhcnRlZApGaXJzdCwgbGF1bmNoIEN5dG9zY2FwZSBhbmQga2VlcCBpdCBydW5uaW5nIHdoZW5ldmVyIHVzaW5nIFJDeTMuIENvbmZpcm0gdGhhdCB5b3UgaGF2ZSBldmVyeXRoaW5nIGluc3RhbGxlZCBhbmQgcnVubmluZzoKYGBge3J9CmN5dG9zY2FwZVBpbmcoKQpjeXRvc2NhcGVWZXJzaW9uSW5mbygpCmBgYAoKIyBMb2FkaW5nIE5ldHdvcmsKTGV0J3Mgb3BlbiBhIEN5dG9zY2FwZSBkZW1vIHNlc3Npb24gZmlsZTogCgpgYGB7cn0Kb3BlblNlc3Npb24oKQpgYGAKCiMgVmlzdWFsaXppbmcgRXhwcmVzc2lvbiBEYXRhIG9uIE5ldHdvcmtzCgpQcm9iYWJseSB0aGUgbW9zdCBjb21tb24gdXNlIG9mIGV4cHJlc3Npb24gZGF0YSBpbiBDeXRvc2NhcGUgaXMgdG8gc2V0IHRoZSAqKnZpc3VhbCBzdHlsZSoqIG9mIHRoZSBub2RlcyAoY29sb3IsIHNoYXBlLCBib3JkZXIpIGluIGEgbmV0d29yayBhY2NvcmRpbmcgdG8gYXZhaWxhYmxlIGRhdGEuIFRoaXMgY3JlYXRlcyBhIHBvd2VyZnVsIHZpc3VhbGl6YXRpb24sIHBvcnRyYXlpbmcgZnVuY3Rpb25hbCByZWxhdGlvbiBhbmQgZXhwZXJpbWVudGFsIHJlc3BvbnNlIGF0IHRoZSBzYW1lIHRpbWUuIEhlcmUsIHdlIHdpbGwgc2hvdyBhbiBleGFtcGxlIG9mIGRvaW5nIHRoaXMuCgpUaGUgZGF0YSB1c2VkIGluIHRoaXMgZXhhbXBsZSBpcyBmcm9tIHllYXN0LCBhbmQgcmVwcmVzZW50cyBhbiBleHBlcmltZW50IG9mIHBlcnR1cmJhdGlvbnMgb2YgdGhlIGdlbmVzICoqR2FsMSoqLCAqKkdhbDQqKiwgYW5kICoqR2FsODAqKiwgd2hpY2ggYXJlIGFsbCB5ZWFzdCB0cmFuc2NyaXB0aW9uIGZhY3RvcnMuCgpGb3IgdGhpcyB0dXRvcmlhbCwgdGhlIGV4cGVyaW1lbnRhbCBkYXRhIHdhcyBwYXJ0IG9mIHRoZSBDeXRvc2NhcGUgc2Vzc2lvbiBmaWxlIHlvdSBsb2FkZWQgZWFybGllciwgYW5kIGlzIHZpc2libGUgaW4gdGhlIE5vZGUgVGFibGU6Cgo8Y2VudGVyPgohW10oLi9kYXRhL2ltZy9iYXNpYy1kYXRhdml6LWdhbGJyb3dzZTMucG5nKQo8L2NlbnRlcj4KCllvdSBjYW4gc2VsZWN0IG5vZGVzIGluIHRoZSBuZXR3b3JrIGJ5IAoKYGBge3J9CnNlbGVjdE5vZGVzKGMoIllETDE5NFciLCAiWUxSMzQ1VyIpLCBieS5jb2wgPSAibmFtZSIpCmBgYAoKU2VsZWN0aW5nIG9uZSBvciBtb3JlIG5vZGVzIGluIHRoZSBuZXR3b3JrIHdpbGwgdXBkYXRlIHRoZSBOb2RlIFRhYmxlIHRvIHNob3cgb25seSB0aGUgY29ycmVzcG9uZGluZyByb3cocykuCgpXZSBjYW4gbm93IHVzZSB0aGUgZGF0YSB0byBtYW5pcHVsYXRlIHRoZSB2aXN1YWwgcHJvcGVydGllcyBvZiB0aGUgbmV0d29yayBieSBtYXBwaW5nIHNwZWNpZmljIGRhdGEgY29sdW1ucyB0byB2aXN1YWwgc3R5bGUgcHJvcGVydGllczoKClRoZSAqKmdhbDgwUmV4cCoqIGV4cHJlc3Npb24gdmFsdWVzIHdpbGwgYmUgbWFwcGVkIHRvIG5vZGUgY29sb3I7IG5vZGVzIHdpdGggbG93IGV4cHJlc3Npb24gd2lsbCBiZSBjb2xvcmVkIGJsdWUsIG5vZGVzIHdpdGggaGlnaCBleHByZXNzaW9uIHdpbGwgYmUgY29sb3JlZCByZWQuClNpZ25pZmljYW5jZSBmb3IgZXhwcmVzc2lvbiB2YWx1ZXMgd2lsbCBiZSBtYXBwZWQgdG8gTm9kZSBCb3JkZXIgV2lkdGgsIHNvIG5vZGVzIHdpdGggc2lnbmlmaWNhbnQgY2hhbmdlcyB3aWxsIGFwcGVhciB3aXRoIGEgdGhpY2tlciBib3JkZXIuCgojIyBTZXQgTm9kZSBGaWxsIENvbG9yCgpMZXQncyBzcGVjaWZ5IHRoZSBub2RlIGZpbGwgY29sb3IgYXMgYSBncmFkaWVudCByYW5naW5nIGZyb20gKipibHVlIHRvIHJlZCoqIGZvciBleHByZXNzaW9uIHZhbHVlcyB1c2luZyBhIGNvbnRpbnVvdXMgbWFwcGluZyBmb3IgdGhlICdnYWw4MFJleHAnIGNvbHVtbjoKCmBgYHtyfQpnYWw4MFJleHAuc2NvcmUudGFibGUgPC0gZ2V0VGFibGVDb2x1bW5zKCdub2RlJywnZ2FsODBSZXhwJykKZ2FsODBSZXhwLm1pbiA8LSBtaW4oZ2FsODBSZXhwLnNjb3JlLnRhYmxlLCBuYS5ybSA9IFQpCmdhbDgwUmV4cC5tYXggPC0gbWF4KGdhbDgwUmV4cC5zY29yZS50YWJsZSwgbmEucm0gPSBUKQpkYXRhLnZhbHVlcyA9IGMoZ2FsODBSZXhwLm1pbiwgMCwgZ2FsODBSZXhwLm1heCkKZGlzcGxheS5icmV3ZXIuYWxsKGxlbmd0aChkYXRhLnZhbHVlcyksIGNvbG9yYmxpbmRGcmllbmRseT1UUlVFLCB0eXBlPSJkaXYiKSAjIGRpdixxdWFsLHNlcSxhbGwKbm9kZS5jb2xvcnMgPC0gYyhyZXYoYnJld2VyLnBhbChsZW5ndGgoZGF0YS52YWx1ZXMpLCAiUmRCdSIpKSkKc2V0Tm9kZUNvbG9yTWFwcGluZygnZ2FsODBSZXhwJywgZGF0YS52YWx1ZXMsIG5vZGUuY29sb3JzLCBzdHlsZS5uYW1lPSJnYWxGaWx0ZXJlZCBTdHlsZSIpCmBgYAoKVGhpcyBwcm9kdWNlcyBhbiBpbml0aWFsIGdyYWRpZW50IHJhbmdpbmcgZnJvbSBibHVlIHRvIHJlZCBmb3IgZXhwcmVzc2lvbiB2YWx1ZXMuIE5vdGljZSB0aGF0IHRoZSBub2RlcyBpbiB0aGUgbmV0d29yayBjaGFuZ2UgY29sb3IuCgo8Y2VudGVyPgohW10oLi9kYXRhL2ltZy9iYXNpYy1kYXRhdml6LW5vZGVmaWxsLnBuZykKPC9jZW50ZXI+CgojIyBTZXQgRGVmYXVsdCBOb2RlIENvbG9yCgpTb21lIG5vZGVzIGluIHRoZSBuZXR3b3JrIGRvbid0IGhhdmUgYW55IGRhdGEsIGFuZCBmb3IgdGhvc2Ugbm9kZXMsIHRoZSBkZWZhdWx0IGNvbG9yIGFwcGxpZXMuIEluIG91ciBjYXNlLCB0aGUgZGVmYXVsdCBjb2xvciBpcyBibHVlLCB3aGljaCBmYWxscyB3aXRoaW4gdGhlIHNwZWN0cnVtIG9mIG91ciBibHVlLXJlZCBncmFkaWVudC4gVGhpcyBpcyBub3QgaWRlYWwgZm9yIGRhdGEgdmlzdWFsaXphdGlvbiwgc28gYSB1c2VmdWwgdHJpY2sgaXMgdG8gY2hvb3NlIGEgY29sb3Igb3V0c2lkZSB0aGUgZ3JhZGllbnQgc3BlY3RydW0gdG8gZGlzdGluZ3Vpc2ggbm9kZXMgd2l0aCBubyBkZWZpbmVkIGV4cHJlc3Npb24gdmFsdWUuCgpXZSBjYW4gc2V0IGRlZmF1bHQgbm9kZSBjb2xvciB0byBsaWdodCBncmF5IGNvbG9yOgoKYGBge3J9CnNldE5vZGVDb2xvckRlZmF1bHQoJyNEM0QzRDMnLCBzdHlsZS5uYW1lID0gImdhbEZpbHRlcmVkIFN0eWxlIikKYGBgCgojIyBTZXQgTm9kZSBCb3JkZXIgV2lkdGgKClRvIHZpc3VhbGl6ZSB0aGUgc2lnbmlmaWNhbmNlIG9mIG1lYXN1cmVtZW50cywgbGV0J3MgYWRkIGEgY29udGl1b3VzIG1hcHBpbmcgZm9yICdnYWw4MFJzaWcnIHAtdmFsdWVzIHRvIE5vZGUgQm9yZGVyIFdpZHRoIGFuZCBOb2RlIEJvcmRlciBDb2xvcjoKYGBge3J9CmdhbDgwUnNpZy5zY29yZS50YWJsZSA8LSBnZXRUYWJsZUNvbHVtbnMoJ25vZGUnLCdnYWw4MFJzaWcnKQpnYWw4MFJzaWcubWluIDwtIG1pbihnYWw4MFJzaWcuc2NvcmUudGFibGUsIG5hLnJtID0gVCkKZ2FsODBSc2lnLm1heCA8LSBtYXgoZ2FsODBSc2lnLnNjb3JlLnRhYmxlLCBuYS5ybSA9IFQpCmRhdGEudmFsdWVzIDwtIGMoZ2FsODBSc2lnLm1pbiwgMC4wNSwgZ2FsODBSc2lnLnNpZykKYm9yZGVyLmNvbG9ycyA8LSBjKHJldihicmV3ZXIucGFsKDMsICJSZWRzIikpKQpib3JkZXIud2lkdGggPC0gYygxMCwxMCwyKQoKc2V0Tm9kZUJvcmRlckNvbG9yTWFwcGluZygnZ2FsODBSc2lnJyxkYXRhLnZhbHVlcyxib3JkZXIuY29sb3JzLHN0eWxlLm5hbWU9ImdhbEZpbHRlcmVkIFN0eWxlIikKc2V0Tm9kZUJvcmRlcldpZHRoTWFwcGluZygnZ2FsODBSc2lnJyxkYXRhLnZhbHVlcyxib3JkZXIud2lkdGgsc3R5bGUubmFtZT0iZ2FsRmlsdGVyZWQgU3R5bGUiKQpgYGAKClRoZSBzaWduaWZpY2FudCBub2RlIG5vdyBoYXZlIGEgdGhpY2tlciwgZGFyayByZWQgYm9yZGVyOgoKPGNlbnRlcj4KIVtdKC4vZGF0YS9pbWcvYmFzaWMtZGF0YXZpei1kYXRhbWFwcGVkLnBuZykKPC9jZW50ZXI+CgojIExheW91dHMKQW4gaW1wb3J0YW50IGFzcGVjdCBvZiBuZXR3b3JrIHZpc3VhbGl6YXRpb24gaXMgdGhlIGxheW91dCwgbWVhbmluZyB0aGUgcG9zaXRpb25pbmcgb2Ygbm9kZXMgYW5kIGVkZ2VzLiBPdXIgbmV0d29yayBoYWQgYSBwcmVzZXQgbGF5b3V0IGluIHRoZSBvcmlnaW5hbCBmaWxlIHlvdSBpbXBvcnRlZCwgYnV0IHRoaXMgY2FuIGJlIGNoYW5nZWQuCgotIExldCdzIGNoYW5nZSB0aGUgbGF5b3V0IHRvICoqRGVncmVlIFNvcnRlZCBDaXJjbGUgTGF5b3V0Kio6CgpgYGB7cn0KbGF5b3V0TmV0d29yaygiZGVncmVlLWNpcmNsZSIpCmBgYAoKPGNlbnRlcj4KIVtdKC4vZGF0YS9pbWcvYmFzaWMtZGF0YXZpei1kZWdyZWUucG5nKQo8L2NlbnRlcj4KSW4gdGhpcyBsYXlvdXQsIG5vZGVzIGFyZSBzb3J0ZWQgYnkgZGVncmVlIChjb25uZWN0ZWRuZXNzKSwgd2l0aCB0aGUgaGlnaGVzdCBkZWdyZWUgbm9kZSBhdCB0aGUgNiBvJ2Nsb2NrIHBvc2l0aW9uLCBhbmQgcmVtYWluaW5nIG5vZGVzIGFyZSBzb3J0ZWQgY291bnRlciBjbG9jay13aXNlIGJhc2VkIG9uIGRlY3JlYXNpbmcgZGVncmVlLgoKRm9yIHRoaXMgbmV0d29yaywgYSBkZWdyZWUtc29ydGVkIGNpcmNsZSBsYXlvdXQgbWF5IG5vdCBiZSB0aGUgbW9zdCBlZmZlY3RpdmUuIEluc3RlYWQsIGxldCdzIHRyeSBhIGZvcmNlLWRpcmVjdGVkIGxheW91dCBpbnN0ZWFkLCB3aGljaCBtYXkgd29yayBiZXR0ZXIgd2l0aCB0aGlzIG5ldHdvcmsuCgpgYGB7cn0KbGF5b3V0TmV0d29yaygiZm9yY2UtZGlyZWN0ZWQiKQpgYGAKCjxjZW50ZXI+CiFbXSguL2RhdGEvaW1nL2Jhc2ljLWRhdGF2aXotZm9yY2VkaXJlY3RlZC5wbmcpCjwvY2VudGVyPgoKQ3l0b3NjYXBlIHN1cHBvcnRzIG1hbnkgZGlmZmVyZW50IGxheW91dCBhbGdvcml0aG1zLCBkZXNjcmliZWQgaW4gZGV0YWlsIGluIHRoZSBbQ3l0b3NjYXBlIG1hbnVhbF0oaHR0cDovL21hbnVhbC5jeXRvc2NhcGUub3JnL2VuL3N0YWJsZS9OYXZpZ2F0aW9uX2FuZF9MYXlvdXQuaHRtbD9oaWdobGlnaHQ9bGF5b3V0I2F1dG9tYXRpYy1sYXlvdXQtYWxnb3JpdGhtcykuCgojIFNlbGVjdCBOb2RlcwoKQ3l0b3NjYXBlIGFsbG93cyB5b3UgdG8gZWFzaWx5IGZpbHRlciBhbmQgc2VsZWN0IG5vZGVzIGFuZCBlZGdlcyBiYXNlZCBvbiBkYXRhIGF0dHJpYnV0ZXMuIE5leHQsIHdlIHdpbGwgc2VsZWN0IGEgc3Vic2V0IG9mIG5vZGVzIHdpdGggaGlnaCBleHByZXNzaW9uIGluIHRoZSBnYWw4MCBrbm9ja291dDoKCkxldCdzIHNlbGVjdCBhIHN1YnNldCBvZiBub2RlcyB3aXRoIGhpZ2ggZXhwcmVzc2lvbiBpbiB0aGUgZ2FsODAga25vY2tvdXQ6IAoKCmBgYHtyfQpjcmVhdGVDb2x1bW5GaWx0ZXIoJ215RmlsdGVyJywgJ2dhbDgwUmV4cCcsIDIuMDAsICJHUkVBVEVSX1RIQU4iKQpgYGAKCllvdSBzaG91bGQgbm93IHNlZSBvbmx5IGEgZmV3IG5vZGVzIGluIHRoZSBuZXR3b3JrIHNlbGVjdGVkIChoaWdobGlnaHRlZCB5ZWxsb3cpLgoKIyMgRXhwYW5kIFNlbGVjdGlvbiBhbmQgQ3JlYXRlIE5ldyBOZXR3b3JrCgpXZSBoYXZlIG5vdyBzZWxlY3RlZCBvbmx5IHRoZSBmZXcgdG9wIGV4cHJlc3Npbmcgbm9kZXMuIFRvIHNlZSB0aGUgY29udGV4dCBvZiB0aGVzZSBub2RlcyBpbiB0aGUgbGFyZ2VyIG5ldHdvcmssIHdlIGNhbiBleHBhbmQgdGhlIHNlbGVjdGlvbiBvZiBub2RlcyB0byBpbmNsdWRlIHRoZSBub2RlcyBjb25uZWN0aW5nIHRvIHRoZSBzZWxlY3RlZCBub2RlcywgaS5lLiB0aGUgZmlyc3QgbmVpZ2hib3JzLiBPbmNlIHdlIGhhdmUgdGhhdCBsYXJnZXIgc2VsZWN0aW9uLCB3ZSBjYW4gY3JlYXRlIGEgbmV3IG5ldHdvcmsuCgpTZWxlY3QgdGhlIGZpcnN0IG5laWdoYm9ycyBvZiBzZWxlY3RlZCBub2RlczoKCmBgYHtyfQpzZWxlY3RGaXJzdE5laWdoYm9ycygpCmBgYAoKV2UgY2FuIG5vdyBjcmVhdGUgYSBuZXR3b3JrIGZyb20gdGhlIHNlbGVjdGlvbjoKCmBgYHtyfQpjcmVhdGVTdWJuZXR3b3JrKG5vZGVzPSJzZWxlY3RlZCIsIHN1Ym5ldHdvcmsubmFtZT0nZ2FsRmlsdGVyZWQgc3ViJykKYGBgCgo8Y2VudGVyPgohW10oLi9kYXRhL2ltZy9iYXNpYy1kYXRhdml6LXN1Ym5ldC5wbmcpCjwvY2VudGVyPgoKRGlnZ2luZyBpbnRvIHRoZSBiaW9sb2d5IG9mIHRoaXMgbmV0d29yaywgaXQgdHVybnMgb3V0IHRoYXQgR0FMNCBpcyByZXByZXNzZWQgYnkgR0FMODAuIEJvdGggbm9kZXMgKEdBTDQgYW5kIEdBTDExKSBzaG93IGZhaXJseSBzbWFsbCBjaGFuZ2VzIGluIGV4cHJlc3Npb24sIGFuZCBuZWl0aGVyIGNoYW5nZSBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50OiB0aGV5IGFyZSBwYWxlIGJsdWUgd2l0aCB0aGluIGJvcmRlcnMuIFRoZXNlIHNsaWdodCBjaGFuZ2VzIGluIGV4cHJlc3Npb24gc3VnZ2VzdCB0aGF0IHRoZSBjcml0aWNhbCBjaGFuZ2UgYWZmZWN0aW5nIHRoZSByZWQgbm9kZXMgbWlnaHQgYmUgc29tZXdoZXJlIGVsc2UgaW4gdGhlIG5ldHdvcmssIGFuZCBub3QgZWl0aGVyIG9mIHRoZXNlIG5vZGVzLiBHQUw0IGludGVyYWN0cyB3aXRoIEdBTDgwLCB3aGljaCBzaG93cyBhIHNpZ25pZmljYW50IGxldmVsIG9mIHJlcHJlc3Npb246IGl0IGlzIG1lZGl1bSBibHVlIHdpdGggYSB0aGlja2VyIGJvcmRlci4KCk5vdGUgdGhhdCB3aGlsZSBHQUw4MCBzaG93cyBldmlkZW5jZSBvZiBzaWduaWZpY2FudCByZXByZXNzaW9uLCBtb3N0IG5vZGVzIGludGVyYWN0aW5nIHdpdGggR0FMNCBzaG93IHNpZ25pZmljYW50IGxldmVscyBvZiBpbmR1Y3Rpb246IHRoZXkgYXJlIHJlbmRlcmVkIGFzIHJlZCByZWN0YW5nbGVzLiBHQUwxMSBpcyBhIGdlbmVyYWwgdHJhbnNjcmlwdGlvbiBjby1mYWN0b3Igd2l0aCBtYW55IGludGVyYWN0aW9ucy4KClB1dHRpbmcgYWxsIG9mIHRoaXMgdG9nZXRoZXIsIHdlIHNlZSB0aGF0IHRoZSAqKip0cmFuc2NyaXB0aW9uYWwgYWN0aXZhdGlvbiBhY3Rpdml0eSBvZiBHYWw0IGlzIHJlcHJlc3NlZCBieSBHYWw4MCoqKi4gU28sIHJlcHJlc3Npb24gb2YgR2FsODAgaW5jcmVhc2VzIHRoZSB0cmFuc2NyaXB0aW9uYWwgYWN0aXZhdGlvbiBhY3Rpdml0eSBvZiBHYWw0LiBFdmVuIHRob3VnaCB0aGUgZXhwcmVzc2lvbiBvZiBHYWw0IGl0c2VsZiBkaWQgbm90IGNoYW5nZSBtdWNoLCAqKip0aGUgR2FsNCB0cmFuc2NyaXB0cyB3ZXJlIG11Y2ggbW9yZSBsaWtlbHkgdG8gYmUgYWN0aXZlIHRyYW5zY3JpcHRpb24gZmFjdG9ycyB3aGVuIEdhbDgwIHdhcyByZXByZXNzZWQqKiouIFRoaXMgZXhwbGFpbnMgd2h5IHRoZXJlIGlzIHNvIG11Y2ggdXAtcmVndWxhdGlvbiBpbiB0aGUgdmljaW5pdHkgb2YgR2FsNC4KCiMgU3VtbWFyeQoKSW4gc3VtbWFyeSwgd2UgaGF2ZToKCi0gRXhwbG9yZWQgYSB5ZWFzdCBpbnRlcmFjdG9tZSBmcm9tIGEgdHJhbnNjcmlwdGlvbiBmYWN0b3Iga25vY2tvdXQgZXhwZXJpbWVudAotIENyZWF0ZWQgYSB2aXN1YWwgc3R5bGUgdXNpbmcgZXhwcmVzc2lvbiB2YWx1ZSBhcyBub2RlIGNvbG9yIGFuZCB3aXRoIGJvcmRlciB3aWR0aCBtYXBwZWQgdG8gc2lnbmlmaWNhbmNlCi0gU2VsZWN0ZWQgaGlnaCBleHByZXNzaW5nIGdlbmVzIGFuZCB0aGVpciBuZWlnaGJvcnMgYW5kIGNyZWF0ZWQgYSBuZXcgbmV0d29yawoKRmluYWxseSwgd2UgY2FuIG5vdyBleHBvcnQgdGhpcyBuZXR3b3JrIGFzIGEgcHVibGljYXRpb24tcXVhbGl0eSBpbWFnZS4uLi4KCiMgU2F2aW5nIFJlc3VsdHMKCkN5dG9zY2FwZSBwcm92aWRlcyBhIG51bWJlciBvZiB3YXlzIHRvIHNhdmUgcmVzdWx0cyBhbmQgdmlzdWFsaXphdGlvbnM6CgotIEFzIGEgc2Vzc2lvbjoKCmBgYHtyfQpzYXZlU2Vzc2lvbignLi9iYXNpYy1kYXRhLXZpc3VhbGl6YXRpb24uY3lzJykKYGBgCgotIEFzIGFuIGltYWdlOgoKYGBge3J9CmV4cG9ydEltYWdlKCcuL2Jhc2ljLWRhdGEtdmlzdWFsaXphdGlvbicsICdQREYnKQpleHBvcnRJbWFnZSgnLi9iYXNpYy1kYXRhLXZpc3VhbGl6YXRpb24nLCAnUE5HJykKZXhwb3J0SW1hZ2UoJy4vYmFzaWMtZGF0YS12aXN1YWxpemF0aW9uJywgJ0pQRUcnKQpleHBvcnRJbWFnZSgnLi9iYXNpYy1kYXRhLXZpc3VhbGl6YXRpb24nLCAnU1ZHJykKZXhwb3J0SW1hZ2UoJy4vYmFzaWMtZGF0YS12aXN1YWxpemF0aW9uJywgJ1BTJykKYGBgCgotIFRvIGEgcHVibGljIHJlcG9zaXRvcnk6CgpgYGAKZXhwb3J0TmV0d29ya1RvTkRFeCgidXNlciIsICJwYXNzIiwgVFJVRSkKYGBgCgotIEFzIGEgZ3JhcGggZm9ybWF0IGZpbGUgKEZvcm1hdHM6ICJDWCBKU09OIiwgIkN5dG9zY2FwZS5qcyBKU09OIiwgIkdyYXBoTUwiLCAiWEdNTUwiLCAiU0lGIiwuLi4pOgoKYGBge3J9CmV4cG9ydE5ldHdvcmsoJy4vYmFzaWMtZGF0YS12aXN1YWxpemF0aW9uJywgJ0NYJykKZXhwb3J0TmV0d29yaygnLi9iYXNpYy1kYXRhLXZpc3VhbGl6YXRpb24nLCAnY3lqcycpCmV4cG9ydE5ldHdvcmsoJy4vYmFzaWMtZGF0YS12aXN1YWxpemF0aW9uJywgJ2dyYXBoTUwnKQpleHBvcnROZXR3b3JrKCcuL2Jhc2ljLWRhdGEtdmlzdWFsaXphdGlvbicsICd4R01NTCcpCmV4cG9ydE5ldHdvcmsoJy4vYmFzaWMtZGF0YS12aXN1YWxpemF0aW9uJywgJ1NJRicpCmBgYAo=